package com.lazyframework.security.web.extra.weixin;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.lazyframework.security.web.exception.AuthenticationException;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.HttpClients;

import java.io.IOException;
import java.util.Map;

/**
 * 微信认证服务，向微信服务器发送认证请求
 * <p>
 * Create by 0x4D6E in 2020.09.09
 */
public class WxAuthService {

    /**
     * 重试次数，默认3次
     */
    private int retryCount;

    private String appid;

    private String secret;

    public WxAuthService(String appid, String secret) {
        this.appid = appid;
        this.secret = secret;
    }

    /**
     * 发送微信认证请求
     *
     * @param url    认证URL
     * @param code   认证凭证
     * @return WxAuthResult
     * @throws IOException
     * @throws OpenIDAuthenticationException
     */
    public WxAuthResult authRequest(String url, String code)
            throws IOException, OpenIDAuthenticationException {

        HttpClient http = HttpClients.createDefault();
        String uri = String.format(url, appid, secret, code);

        ObjectMapper objectMapper = new ObjectMapper();
        HttpResponse response = http.execute(new HttpGet(uri));

        // 字节流转成Map对象
        Map<String, String> reqResult =
                objectMapper.readValue(response.getEntity().getContent(), new TypeReference<Map<String, String>>() {
                });

        if (!reqResult.containsKey("errcode")) {
            // 最终返回一个Java对象
            return objectMapper.readValue(objectMapper.writer().writeValueAsString(reqResult), WxAuthResult.class);
        }

        // 错误码，请求成功时不返回
        String errCode = reqResult.get("errcode");

        // 系统繁忙，触发重试机制
        if ("-1".equals(errCode)) {
            if (retryCount <= 3) {
                retryCount++;
                return authRequest(url, code);
            }
        }

        throw new OpenIDAuthenticationException(errCode, reqResult.get("errmsg"));
    }

    /**
     * 给微信发送认证请求
     *
     * @param code     认证凭证
     * @param authType 认证类型：小程序/网页授权
     * @return
     * @throws IOException
     * @throws AuthenticationException
     */
    public WxAuthResult auth(String code, WxAuthType authType)
            throws IOException, AuthenticationException {
        if (authType.equals(WxAuthType.MINI_PROGRAM)) {
            return authRequest(WxConst.URI_CODE2SESSION, code);
        }
        if (authType.equals(WxAuthType.MEDIA_PLATFORM)) {
            return authRequest(WxConst.URI_CODE2ACCESS_TOKEN, code);
        }
        throw new IllegalArgumentException("Unable to determine Weixin Authentication Type.");
    }

    /**
     * 微信认证类型
     */
    public enum WxAuthType {

        /**
         * 小程序
         */
        MINI_PROGRAM,

        /**
         * 公众号（网页授权）
         */
        MEDIA_PLATFORM,

        /**
         * 未知认证类型
         */
        NONE

    }

}
